iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
Modern Web

Three.js × WebGL 實戰:打造 HD-2D 像素地下城與即時光影系統系列 第 4 篇

Day 4:賦予像素靈魂與上帝視角!TextureLoader 與 OrbitControls

  • 分享至 

  • xImage
  •  

昨天我們在網頁上成功渲染出了一塊深色的 3D 地板,但作為一個開發中的地下城,我們需要能夠自由地從各個角度檢視場景。此外,純色的幾何體太過單調,今天我們要裝上「上帝視角控制器」,並載入真正的 16-bit 像素圖,將它完美地「拼貼」成一片 HD-2D 風格的復古廣場!

【Step 1:裝上上帝視角 OrbitControls】

Three.js 提供了一個非常實用的工具 OrbitControls(軌道控制器),讓玩家能用滑鼠左鍵拖曳旋轉、右鍵平移、滾輪縮放。

main.js 檔案最上方匯入控制器:

import './style.css';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

接著在宣告 renderer 的下方,加入控制器的設定,並開啟滑順的阻尼慣性:

const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;

【匯入像素紋理與 HD-2D 魔法】

確保你的 public 資料夾中已經準備好一張 16x16 像素的無縫石磚圖,並命名為 floor.png。在 Three.js 中,我們使用 TextureLoader 來載入這張圖片。

關鍵點:WebGL 預設會開啟線性過濾來模糊圖片邊緣,這對 16-bit 像素藝術是致命傷!我們必須強制設定「最近鄰插值 (NearestFilter)」,保持像素的絕對銳利。

const textureLoader = new THREE.TextureLoader();
const floorTexture = textureLoader.load('/floor.png');
// 關閉線性模糊,保持 16-bit 像素的絕對銳利!
floorTexture.magFilter = THREE.NearestFilter;

【無限延伸的無縫拼貼】

如果直接把這張 16x16 的小圖貼到 10x10 的 3D 平面上,圖片會被嚴重拉伸。我們需要開啟「紋理重複平鋪」,讓這張小圖在平面上像貼磁磚一樣完美複製 100 次:

// 開啟水平與垂直的重複平鋪
floorTexture.wrapS = THREE.RepeatWrapping; 
floorTexture.wrapT = THREE.RepeatWrapping; 
// 設定水平與垂直各重複 10 次 (配合 PlaneGeometry 的大小)
floorTexture.repeat.set(10, 10);

【更新材質與渲染迴圈】

將原本純色的材質改為使用剛設定好的貼圖 (map),並在渲染迴圈中持續更新控制器,讓阻尼效果生效:

const geometry = new THREE.PlaneGeometry(10, 10);
const material = new THREE.MeshBasicMaterial({ 
  map: floorTexture, // 套用像素貼圖
  side: THREE.DoubleSide 
});
const floor = new THREE.Mesh(geometry, material);
floor.rotation.x = -Math.PI / 2; 
scene.add(floor);

function animate() {
  requestAnimationFrame(animate);
  controls.update(); // 持續更新控制器
  renderer.render(scene, camera);
}

【本日結語】

為確保進度一致,這是完成 Day 4 後 src/main.js 的完整樣貌:

import './style.css';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// 1. 建立場景與攝影機
const scene = new THREE.Scene();
scene.background = new THREE.Color('#1a1a2e');

const sizes = { width: window.innerWidth, height: window.innerHeight };
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 1000);
camera.position.set(0, 5, 10);
scene.add(camera);

// 2. 建立渲染器
const canvas = document.querySelector('#webgl-canvas');
const renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

// 3. 初始化 OrbitControls
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;

// 4. 載入紋理、關閉模糊並設定重複拼貼
const textureLoader = new THREE.TextureLoader();
const floorTexture = textureLoader.load('/floor.png');
floorTexture.magFilter = THREE.NearestFilter; 
floorTexture.wrapS = THREE.RepeatWrapping; 
floorTexture.wrapT = THREE.RepeatWrapping; 
floorTexture.repeat.set(10, 10);           

// 5. 建立 3D 地板並加入場景
const geometry = new THREE.PlaneGeometry(10, 10);
const material = new THREE.MeshBasicMaterial({ 
  map: floorTexture, 
  side: THREE.DoubleSide 
});
const floor = new THREE.Mesh(geometry, material);
floor.rotation.x = -Math.PI / 2; 
scene.add(floor);

// 6. 啟動渲染迴圈
function animate() {
  requestAnimationFrame(animate);
  controls.update(); 
  renderer.render(scene, camera);
}
animate();

存檔並切換到瀏覽器,用滑鼠拖曳感受一下自由視角,並欣賞那片由 100 塊小石磚完美接合而成的銳利像素廣場。這就是 HD-2D 開發最迷人的第一步!
image



上一篇
Day3 清空畫布!Three.js 創世三神寶與第一片 3D 地板
下一篇
Day 5:點亮地下城!材質升級與動態光源
系列文
Three.js × WebGL 實戰:打造 HD-2D 像素地下城與即時光影系統 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言